﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using System.Web.UI;
using System.Data;

namespace ExtJSExtender
{
    public class Util
    {
        #region Describe属性

        /// <summary>
        /// 将属性添加到JS对象中
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="descriptor"></param>
        public static void DescribeProperties(object obj, ScriptControlDescriptor descriptor)
        {
            Type t = obj.GetType();
            PropertyInfo[] info = t.GetProperties();

            foreach (PropertyInfo f in info)
            {
                var descProps = f.GetCustomAttributes(typeof(DescribableProperty), true);//.GetCustomAttribute<DescribableProperty>(true);
                if (descProps != null && descProps.Length > 0)
                {
                    DescribableProperty descProp = descProps[0] as DescribableProperty;
                    string name = string.IsNullOrEmpty(descProp.Name) ? f.Name : descProp.Name;
                    descriptor.AddProperty(name, f.GetValue(obj, null));
                }
            }
        }

        #endregion

        #region 序列化与反序列化JSON

        private static JavaScriptSerializer serializer = null;
        public static JavaScriptSerializer Serializer
        {
            get
            {
                if (serializer == null)
                {
                    serializer = new JavaScriptSerializer();
                    serializer.RegisterConverters(new JavaScriptConverter[] { 
                        new TreeNodeJSConverter(), 
                        new DataTableJSConverter(), 
                        new DataRowJSConverter(),
                        new DataColumnJSConverter(),
                        new GridColumnJSConverter(),
                        new FormFieldConfigJSConverter(),
                        new GridColumnActionItemJSConverter(),
                    });
                    serializer.MaxJsonLength = 8 * 1024 * 1024;     // 该属性是字符串的长度，如果是Unicode编码，其大小应为16MB
                }
                return serializer;
            }
        }

        public static string SerializeToJson(Object obj)
        {
            return Serializer.Serialize(obj);
        }

        public static T DeserializeJson<T>(string jsonString)
        {
            return Serializer.Deserialize<T>(jsonString);
        }

        #endregion

        #region 输出辅助方法

        /// <summary>
        /// 向浏览器输出一个Hidden标签
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static void RenderHiddenField(HtmlTextWriter writer, string key, string value)
        {
            writer.Write("<input type='hidden' id='");
            writer.Write(key);
            writer.Write("' name='");
            writer.Write(key);
            writer.Write("' value='");
            writer.Write(value);
            writer.Write("' />");
        }

        #endregion
    }

    #region 树节点的序列化和反序列化

    /// <summary>
    /// TreeNode的JSON转化器
    /// </summary>
    public class TreeNodeJSConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.TreeNode node = new ExtJSExtender.Controls.TreeNode();

            object value = null;
            if (dictionary.TryGetValue("id", out value))
                node.id = (string)value;
            if (dictionary.TryGetValue("text", out value))
                node.text = (string)value;
            if (dictionary.TryGetValue("parentId", out value))
                node.parentId = (string)value;
            if (dictionary.TryGetValue("leaf", out value))
                node.leaf = (bool?)value;
            if (dictionary.TryGetValue("expanded", out value))
                node.expanded = (bool?)value;
            if (dictionary.TryGetValue("checked", out value))
                node.isChecked = (bool?)value;
            if (dictionary.TryGetValue("cls", out value))
                node.cls = (string)value;
            if (dictionary.TryGetValue("icon", out value))
                node.icon = (string)value;
            if (dictionary.TryGetValue("href", out value))
                node.href = (string)value;
            if (dictionary.TryGetValue("hrefTarget", out value))
                node.hrefTarget = (string)value;
            if (dictionary.TryGetValue("draggable", out value))
                node.draggable = (bool?)value;
            if (dictionary.TryGetValue("loaded", out value))
                node.loaded = (bool)value;

            //递归遍历子节点
            if (dictionary.TryGetValue("children", out value))
            {
                if (value != null && value.GetType() == typeof(ArrayList))
                {
                    var list = (ArrayList)value;

                    node.children = new Controls.TreeNodeCollection();
                    foreach (Dictionary<string, object> item in list)
                    {
                        node.children.Add((Controls.TreeNode)this.Deserialize(item, type, serializer));
                    }
                }
                else
                {
                    node.children = null;
                }
            }

            return node;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.TreeNode node = obj as ExtJSExtender.Controls.TreeNode;
            Dictionary<string, object> result = new Dictionary<string, object>();
            if (!string.IsNullOrWhiteSpace(node.id))
                result.Add("id", node.id);
            if (!string.IsNullOrWhiteSpace(node.text))
                result.Add("text", node.text);
            if (!string.IsNullOrWhiteSpace(node.parentId))
                result.Add("parentId", node.parentId);

            if (node.leaf.HasValue)
                result.Add("leaf", node.leaf);

            result.Add("loaded", node.loaded);
            if (node.children != null && node.children.Count > 0)
                result.Add("children", node.children);
            else if (node.loaded)
                result.Add("children", node.children);

            if (node.expanded.HasValue)
                result.Add("expanded", node.expanded);
            if (node.isChecked.HasValue)
                result.Add("checked", node.isChecked);
            if (!string.IsNullOrWhiteSpace(node.cls))
                result.Add("cls", node.cls);
            if (!string.IsNullOrWhiteSpace(node.icon))
                result.Add("icon", node.icon);
            if (!string.IsNullOrWhiteSpace(node.href))
                result.Add("href", node.href);
            if (!string.IsNullOrWhiteSpace(node.hrefTarget))
                result.Add("hrefTarget", node.hrefTarget);
            if (node.draggable.HasValue)
                result.Add("draggable", node.draggable);

            return result;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new Type[] { typeof(ExtJSExtender.Controls.TreeNode) };
            }
        }
    }

    #endregion

    #region GridPanel列、GridColumnActionItem的序列化

    public class GridColumnJSConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.GridColumn column = new ExtJSExtender.Controls.GridColumn();

            object value = null;
            if (dictionary.TryGetValue("text", out value))
                column.HeaderText = (string)value;
            if (dictionary.TryGetValue("dataIndex", out value))
                column.DataField = (string)value;
            if (dictionary.TryGetValue("width", out value))
                column.Width = (double)value;
            if (dictionary.TryGetValue("flex", out value))
                column.Flex = (double)value;
            if (dictionary.TryGetValue("sortable", out value))
                column.Sortable = (bool)value;
            if (dictionary.TryGetValue("hideable", out value))
                column.Hideable = (bool)value;
            if (dictionary.TryGetValue("hidden", out value))
                column.Hidden = (bool)value;
            if (dictionary.TryGetValue("editor", out value))
                column.EditorConfig = serializer.Deserialize<ExtJSExtender.Controls.FormFieldConfig>((string)value);
            if (dictionary.TryGetValue("xtype", out value))
            {
                Controls.GridColumnXType xtype = Controls.GridColumnXType.none;
                if (Enum.TryParse<Controls.GridColumnXType>(value.ToString(), false, out xtype))
                    column.Type = xtype;
            }

            return column;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.GridColumn column = obj as ExtJSExtender.Controls.GridColumn;
            Dictionary<string, object> result = new Dictionary<string, object>();

            if (!string.IsNullOrWhiteSpace(column.HeaderText))
                result.Add("text", column.HeaderText);
            if (!string.IsNullOrWhiteSpace(column.DataField))
                result.Add("dataIndex", column.DataField);
            if (column.Width.HasValue && column.Width.Value > 0)
                result.Add("width", column.Width.Value);
            if (column.Flex.HasValue && column.Flex.Value > 0)
                result.Add("flex", column.Flex.Value);
            if (column.Sortable.HasValue)
                result.Add("sortable", column.Sortable.Value);
            if (column.Hideable.HasValue)
                result.Add("hideable", column.Hideable.Value);
            if (column.Hidden.HasValue)
                result.Add("hidden", column.Hidden.Value);
            if (column.EditorConfig != null)
                result.Add("editor", column.EditorConfig);
            if (column.Type != Controls.GridColumnXType.none)
                result.Add("xtype", column.Type.ToString());

            //根据xtype解析数据
            switch (column.Type)
            {
                case ExtJSExtender.Controls.GridColumnXType.none:
                    break;
                case ExtJSExtender.Controls.GridColumnXType.actioncolumn:
                    {
                        if (column.ActionItems != null && column.ActionItems.Count > 0)
                        {
                            result.Add("items", column.ActionItems);
                        }
                    }
                    break;
                case ExtJSExtender.Controls.GridColumnXType.booleancolumn:
                    break;
                case ExtJSExtender.Controls.GridColumnXType.datecolumn:
                    break;
                case ExtJSExtender.Controls.GridColumnXType.numbercolumn:
                    break;
                case ExtJSExtender.Controls.GridColumnXType.templatecolumn:
                    break;
                default:
                    break;
            }

            return result;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new Type[] { typeof(ExtJSExtender.Controls.GridColumn) };
            }
        }
    }

    public class GridColumnActionItemJSConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.GridColumnActionItem actionItem = new Controls.GridColumnActionItem();

            object value = null;
            if (dictionary.TryGetValue("actionName", out value))
                actionItem.ActionName = (string)value;
            if (dictionary.TryGetValue("icon", out value))
                actionItem.IconUrl = (string)value;
            if (dictionary.TryGetValue("iconCls", out value))
                actionItem.IconClassName = (string)value;
            if (dictionary.TryGetValue("tooltip", out value))
                actionItem.Tooltip = (string)value;
            if (dictionary.TryGetValue("disabled", out value))
                actionItem.Disabled = (bool)value;

            return actionItem;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.GridColumnActionItem actionItem = obj as ExtJSExtender.Controls.GridColumnActionItem;
            Dictionary<string, object> result = new Dictionary<string, object>();

            if (!string.IsNullOrEmpty(actionItem.ActionName))
                result.Add("actionName", actionItem.ActionName);
            if (!string.IsNullOrEmpty(actionItem.IconUrl))
                result.Add("icon", actionItem.IconUrl);
            if (!string.IsNullOrEmpty(actionItem.Tooltip))
                result.Add("tooltip", actionItem.Tooltip);
            if (!string.IsNullOrEmpty(actionItem.IconClassName))
                result.Add("iconCls", actionItem.IconClassName);

            result.Add("disabled", actionItem.Disabled);

            return result;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new Type[] { typeof(ExtJSExtender.Controls.GridColumnActionItem) };
            }
        }
    }

    #endregion

    #region FormFieldConfig 序列化和反序列化

    public class FormFieldConfigJSConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.FormFieldConfig editorConfig = new Controls.FormFieldConfig();

            object value = null;
            if (dictionary.TryGetValue("xtype", out value))
            {
                editorConfig.Type = (ExtJSExtender.Controls.FormFieldXType)Enum.Parse(
                    typeof(ExtJSExtender.Controls.FormFieldXType), value.ToString());
            }
            if (dictionary.TryGetValue("allowBlank", out value))
                editorConfig.AllowBlank = (bool)value;

            return editorConfig;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            ExtJSExtender.Controls.FormFieldConfig editorConfig = obj as ExtJSExtender.Controls.FormFieldConfig;
            Dictionary<string, object> result = new Dictionary<string, object>();

            result.Add("xtype", editorConfig.Type.ToString());
            if (editorConfig.AllowBlank.HasValue)
                result.Add("allowBlank", editorConfig.AllowBlank.Value);

            return result;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new Type[] { typeof(ExtJSExtender.Controls.FormFieldConfig) };
            }
        }
    }

    #endregion

    #region DataTable、DataRow的序列化和反序列化

    public class DataColumnJSConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            DataColumn column = obj as DataColumn;

            Dictionary<string, object> tmpDict = new Dictionary<string, object>();
            tmpDict.Add("name", column.ColumnName);
            tmpDict.Add("type", column.DataType.Name);

            return tmpDict;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new Type[] { typeof(DataColumn) }; }
        }
    }


    public class DataRowJSConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            DataRow row = obj as DataRow;

            Dictionary<string, object> tmpDict = new Dictionary<string, object>();
            foreach (DataColumn dc in row.Table.Columns)
            {
                tmpDict.Add(dc.ColumnName, row[dc]);
            }

            return tmpDict;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new Type[] { typeof(DataRow) }; }
        }
    }

    public class DataTableJSConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            DataTable table = obj as DataTable;
            if (table == null)
                return null;

            Dictionary<string, object> result = new Dictionary<string, object>();
            result.Add("Rows", table.Rows);
            result.Add("Columns", table.Columns);
            result.Add("TotalItems", table.Rows.Count);

            return result;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new Type[] { typeof(DataTable) }; }
        }
    }



    #endregion

    #region 字段为JS属性的标记

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class DescribableProperty : Attribute
    {
        /// <summary>
        /// 属性在JS中的名称，如果为空，则使用字段名
        /// </summary>
        public string Name { get; set; }

        public DescribableProperty(string name = "")
        {
            this.Name = name;
        }
    }

    #endregion
}
